/*
 * Copyright (c) 2009-2012 Panxiaobo
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package p.cod;

import java.util.List;

import p.cod.CodFileReader.Clz;
import p.cod.CodFileReader.Field;
import p.cod.CodFileReader.Routine;

import com.googlecode.dex2jar.reader.io.DataIn;

/*package*/class CodeReader implements Opcodes {

    CodFileReader reader;

    public CodeReader(CodFileReader r) {
        super();
        this.reader = r;
    }

    public void read(DataIn in, int codeSize) {
        int base = in.getCurrentPosition();
        int end = base + codeSize;

        int a, b, c;
        Xdump dump = new Xdump(base);
        for (int p = base; p < end; p = in.getCurrentPosition()) {
            int offset = p - base;
            int opcode = in.readUByte();
            if (opcode == OP01XX) {
                opcode = 0x100 + in.readUByte();
            }
            int lib = 0;

            dump.update(offset, opcode);
            switch (opcode) {
            case IRETURN_FIELD_WIDE:
            case ARETURN_FIELD_WIDE:
            case PUTFIELD_RETURN_WIDE:
            case PUTFIELD_WIDE:
            case LPUTFIELD_WIDE:
            case GETFIELD_WIDE:
            case LGETFIELD_WIDE:
            case ALOAD_0_GETFIELD_WIDE:
                a = in.readUByte() + 256;
                dump.s(a);
                break;
            case IRETURN_BIPUSH:
            case IRETURN_FIELD:
            case ARETURN_FIELD:
            case PUTFIELD_RETURN:
            case PUTFIELD:
            case LPUTFIELD:
            case GETFIELD:
            case LGETFIELD:
            case ALOAD_0_GETFIELD:
                a = in.readUByte();
                dump.s(a);
                break;
            case BIPUSH:
            case ILOAD:
            case ALOAD:
            case LLOAD:
            case ISTORE:
            case ASTORE:
            case LSTORE:

            case IF_ICMPEQ:
            case IF_ACMPEQ:
            case IFEQ:
            case IF_ICMPNE:
            case IF_ACMPNE:
            case IFNE:
            case IF_ICMPGT:
            case IFGT:
            case IF_ICMPGE:
            case IFGE:
            case IF_ICMPLT:
            case IFLT:
            case IF_ICMPLE:
            case IFLE:
            case IFNULL:
            case IFNONNULL:
            case GOTO:
            case NEWARRAY:
            case INVOKEVIRTUAL_SHORT:

                a = in.readUByte();
                dump.b(a);
                break;

            case BREAKPOINT:
            case ENTER:
            case XENTER:
            case SYNCH:
            case CLINIT_WAIT:
            case IRETURN:
            case ARETURN:
            case LRETURN:
            case RETURN:
            case CLINIT_RETURN:
            case NOENTER_RETURN:
            case ACONST_NULL:
            case ICONST_0:
            case ICONST_1:
            case ILOAD_0:
            case ILOAD_1:
            case ILOAD_2:
            case ILOAD_3:
            case ILOAD_4:
            case ILOAD_5:
            case ILOAD_6:
            case ILOAD_7:
            case ALOAD_0:
            case ALOAD_1:
            case ALOAD_2:
            case ALOAD_3:
            case ALOAD_4:
            case ALOAD_5:
            case ALOAD_6:
            case ALOAD_7:
            case ISTORE_0:
            case ISTORE_1:
            case ISTORE_2:
            case ISTORE_3:
            case ISTORE_4:
            case ISTORE_5:
            case ISTORE_6:
            case ISTORE_7:
            case ASTORE_0:
            case ASTORE_1:
            case ASTORE_2:
            case ASTORE_3:
            case ASTORE_4:
            case ASTORE_5:
            case ASTORE_6:
            case ASTORE_7:
            case I2B:
            case I2S:
            case I2C:
            case I2L:
            case L2I:
            case INEG:
            case LNEG:
            case IADD:
            case LADD:
            case ISUB:
            case LSUB:
            case IMUL:
            case LMUL:
            case IDIV:
            case LDIV:
            case IREM:
            case LREM:
            case IAND:
            case LAND:
            case IOR:
            case LOR:
            case IXOR:
            case LXOR:
            case ISHL:
            case LSHL:
            case ISHR:
            case LSHR:
            case IUSHR:
            case LUSHR:
            case LCMP:
            case ARRAYLENGTH:
            case BALOAD:
            case SALOAD:
            case CALOAD:
            case IALOAD:
            case AALOAD:
            case LALOAD:
            case BASTORE:
            case CASTORE:
            case SASTORE:
            case IASTORE:
            case AASTORE:
            case LASTORE:
            case ATHROW:
            case MONITORENTER:
            case MONITOREXIT:
            case NOP:
            case POP:
            case POP2:
            case DUP:
            case DUP2:
            case DUP_X1:
            case DUP_X2:
            case DUP2_X1:
            case DUP2_X2:
            case SWAP:
            case ISREAL:
            case STRINGLENGTH:
            case STRINGALOAD:
            case ENTER_NARROW:
            case LDC_NULLSTR:
            case HALT:
            case THREADDEATH:
            case ERROP1:
            case ERROP2:
            case FADD:
            case DADD:
            case FSUB:
            case DSUB:
            case FMUL:
            case DMUL:
            case FDIV:
            case DDIV:
            case FREM:
            case DREM:
            case FNEG:
            case DNEG:
            case I2F:
            case I2D:
            case L2F:
            case L2D:
            case F2I:
            case F2L:
            case F2D:
            case D2I:
            case D2L:
            case D2F:
            case FCMPL:
            case FCMPG:
            case DCMPL:
            case DCMPG:
            case DCONST_0:
            case DCONST_1:
            case FCONST_0:
            case FCONST_1:
            case FCONST_2:
                dump.x();
                break;
            case INVOKESTATIC_LIB:
                lib = in.readUByte();
            case INVOKESTATIC: {
                a = in.readUByte();
                b = in.readUShortx();
                Routine ro = (Routine) reader.fixItems.get(p + 1);
                if (ro == null) {
                    List<Routine> rs = reader.thisModule.rountines;
                    for (Routine routine : rs) {
                        if (routine.offset == b) {
                            ro = routine;
                            break;
                        }
                    }
                }
                dump.lbs(lib, a, b, ro);
                break;
            }
            case INVOKENONVIRTUAL_LIB:
            case INVOKESPECIAL_LIB:
                lib = in.readUByte();
            case INVOKEVIRTUAL:
            case INVOKENONVIRTUAL:
            case INVOKESPECIAL:

            case IINVOKENATIVE:
            case INVOKENATIVE:
            case LINVOKENATIVE: {
                a = in.readUShortx();
                b = in.readUByte();
                Routine ro = (Routine) reader.fixItems.get(p + 1);
                if (ro == null) {
                    List<Routine> rs = reader.thisModule.rountines;
                    for (Routine routine : rs) {
                        if (routine.offset == a) {
                            ro = routine;
                            break;
                        }
                    }
                }
                dump.lsb(lib, a, b, ro);
                break;
            }
            case INVOKEINTERFACE:
                a = in.readUShortx();
                b = in.readUByte();
                c = in.readUShortx();
                dump.sbs(a, b, c);
                break;
            case JUMPSPECIAL_LIB:
                lib = in.readByte();
            case JUMPSPECIAL:
                b = in.readUByte();
                a = in.readUShortx();
                dump.lbs(lib, b, a);
                break;
            case ENTER_WIDE:
            case XENTER_WIDE:
                a = in.readUByte();
                b = in.readUByte();
                c = in.readUByte();
                dump.bbb(a, b, c);
                break;
            case IRETURN_SIPUSH:
            case SIPUSH:
                a = in.readShortx();
                dump.s(a);
                break;
            case IRETURN_IIPUSH:
            case IIPUSH:
                a = in.readIntx();
                dump.i(a);
                break;
            case LIPUSH:
                long va = ((long) in.readIntx() & 0x0FFffFFff) | ((long) in.readIntx() << 32);
                dump.l(va);
                break;
            case LDC:
                a = in.readUShortx();
                String string = reader.getLiteralString(a);
                dump.s(a, string);
                break;
            case LDC_UNICODE:
                b = in.readUShortx();
                a = in.readUShortx();
                dump.ss(b, a);
                break;
            case ARRAYINIT:
                a = in.readUByte();
                b = in.readUShortx();
                c = in.readUShortx();
                dump.bss(a, b, c);
                break;
            case ILOAD_WIDE:
            case ALOAD_WIDE:
            case LLOAD_WIDE:
            case ISTORE_WIDE:
            case ASTORE_WIDE:
            case LSTORE_WIDE:
                a = in.readUShortx();
                dump.s(a);
                break;
            case PUTSTATIC_LIB:
            case LPUTSTATIC_LIB:
            case GETSTATIC_LIB:
            case LGETSTATIC_LIB:
                lib = in.readUByte();
            case PUTSTATIC:
            case LPUTSTATIC:
            case GETSTATIC:
            case LGETSTATIC:
                a = in.readUByte();
                b = in.readUShortx();

                Field field = (Field) reader.fixItems.get(p + 1);
                if (field == null) {
                    if (lib != 255) {
                        Clz clz = reader.thisModule.classes.get(a);
                        field = clz.staticFieldMap.get(b);
                    } else {
                        // TODO
                    }
                }
                dump.lbs(lib, a, b, field);
                break;

            case IINC:
            case INSTANCEOF_ARRAY:
            case CHECKCAST_ARRAY:
                a = in.readUByte();
                b = in.readUByte();
                dump.bb(a, b);
                break;
            case IINC_WIDE:
                a = in.readUShortx();
                b = in.readUShortx();
                dump.ss(a, b);
                break;
            case GOTO_W:
                a = in.readUShortx();
                dump.s(a);
                break;
            case MULTIANEWARRAY:
                a = in.readUByte();
                b = in.readUByte();
                c = in.readUByte();
                dump.bbb(a, b, c);
                break;
            case NEWARRAY_OBJECT_LIB:
            case NEW_LIB:
            case CLINIT_LIB:
            case INSTANCEOF_LIB:
            case CHECKCAST_LIB:
            case LDC_CLASS_LIB:
                lib = in.readUByte();
            case SYNCH_STATIC:
            case NEWARRAY_OBJECT:
            case NEW:
            case CLINIT:
            case INSTANCEOF:
            case CHECKCAST:
            case LDC_CLASS:
                a = in.readUByte();
                Clz clz = (Clz) reader.fixItems.get(p + 1);
                if (clz == null) {
                    clz = reader.thisModule.classes.get(a);
                }
                dump.lb(lib, a, clz);
                break;
            case MULTIANEWARRAY_OBJECT_LIB:
                lib = in.readUByte();
            case MULTIANEWARRAY_OBJECT:
                a = in.readUByte();
                b = in.readUByte();
                c = in.readUByte();
                dump.lbbb(lib, a, b, c);
                break;
            case CHECKCASTBRANCH_LIB:
                lib = in.readUByte();
            case CHECKCASTBRANCH:
                a = in.readUByte();
                b = in.readUShortx();
                dump.lbs(lib, a, b);
                break;
            case CHECKCASTBRANCH_ARRAY:
                c = in.readUByte();
                a = in.readUByte();
                b = in.readUShortx();
                dump.bbs(c, a, b);
                break;
            case INSTANCEOF_ARRAYOBJECT_LIB:
            case CHECKCAST_ARRAYOBJECT_LIB:
                lib = in.readUByte();
            case INSTANCEOF_ARRAYOBJECT:
            case CHECKCAST_ARRAYOBJECT:
                a = in.readUByte();
                b = in.readUByte();
                dump.lbb(lib, a, b);
                break;
            case JMPBACK:
            case JMPFORWARD:
                a = in.readUShortx();
                dump.s(a);
                break;
            case LOOKUPSWITCH_SHORT:
                a = in.readUShortx();
                for (int i = 0; i < a; i++) {
                    b = in.readUShortx();
                    c = in.readUShortx();
                }
                c = in.readUShortx();
                dump.s(a);
                break;
            case LOOKUPSWITCH:
                a = in.readUShortx();
                for (int i = 0; i < a; i++) {
                    b = in.readIntx();
                    c = in.readUShortx();
                }
                c = in.readUShortx();
                dump.s(a);
                break;
            case TABLESWITCH:
                a = in.readShortx();
                b = in.readIntx();
                for (int i = 0; i < a; i++) {
                    c = in.readUShortx();
                }
                dump.s(a);
                break;
            default:
                throw new RuntimeException();
            }

        }
    }
}
